/*
 * Su-yun Information Technology, Co.,Ltd CONFIDENTIAL
 *
 * (C) Copyright Su-yun Information Technology, Co.,Ltd. 2021 All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains the property of
 * Su-yun Information Technology, Co.,Ltd. The intellectual property herein
 * are protected by trade secret, patent law or copyright law. Dissemination
 * of this information or reproduction of this material is strictly forbidden
 * unless prior written permission is obtained from Su-yun Information Technology.
 *
 * https://suyunplat.com/
 *
 */
package com.xtiim.battery.discharge.common.concurrent.pool;

import cn.hutool.core.thread.ThreadFactoryBuilder;
import com.xtiim.battery.discharge.common.Configuration;
import com.xtiim.battery.discharge.common.Constants;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;

/**
 * Global executor for task handling or scheduling.
 * The purpose is to consolidate all thread pools
 * in one place for efficient management.
 *
 */
@Slf4j
public class GlobalExecutor {

    /**
     * default thread pool size
     */
    public static final int DEFAULT_THREAD_POOL_SIZE =
            Runtime.getRuntime().availableProcessors() <= 1 ? 1 : Runtime.getRuntime().availableProcessors();

    public static int collectPeriod = 1000;

    private static int timeoutCheckPeriod = 2000;

    private static int hostDataQueueSize = 10000;

    private static final int FAILED_PORT_CHECK_PERIOD = 1000;

    /**
     * Thread pool for handling response messages for each serial port.
     */
    private ExecutorService handleResponseExecutor;

    /**
     * Thread pool for collect
     */
    private ScheduledExecutorService collectExecutor;

    /**
     * Thread pool for failed port check
     */
    private ScheduledExecutorService failedPortCheckExecutor;

    /**
     * Thread pool for device status change task
     */
    private ExecutorService commandExecutor;

    /**
     * websocket push thread pool
     */
    private ExecutorService websocketExecutor;

    private static GlobalExecutor instance = new GlobalExecutor();

    private GlobalExecutor() {
        init();
    }

    private void init() {
        int collectPeriodParam = Configuration.getInt(Constants.COLLECT_PERIOD);
        if (collectPeriodParam > 0) {
            collectPeriod = collectPeriodParam;
        }

        int timeoutCheckPeriodParam = Configuration.getInt(Constants.TIMEOUT_CHECK_PERIOD);
        if (timeoutCheckPeriodParam > 0) {
            timeoutCheckPeriod = timeoutCheckPeriodParam;
        }

        int hostDataQueueSizeParam = Configuration.getInt(Constants.HOST_DATA_QUEUE_SIZE);
        if (hostDataQueueSizeParam > 0) {
            hostDataQueueSize = hostDataQueueSizeParam;
        }

        handleResponseExecutor = new LtqExecutor<>(1,
                new ThreadFactoryBuilder().setNamePrefix("Resp_Handle_").build(), hostDataQueueSize);

        commandExecutor = new ScheduledThreadPoolExecutor(1,
                new ThreadFactoryBuilder().setNamePrefix("Cmd_Send_").build());

        collectExecutor = new ScheduledThreadPoolExecutor(1,
                new ThreadFactoryBuilder().setNamePrefix("Collect_").build());

        failedPortCheckExecutor = new ScheduledThreadPoolExecutor(1,
                new ThreadFactoryBuilder().setNamePrefix("Failed_Port_Check").build());

        websocketExecutor = Executors.newSingleThreadExecutor(ThreadFactoryBuilder.create().setNamePrefix("Websocket_Push_Task_").build());

    }

    public static GlobalExecutor getInstance() {
        return instance;
    }

    public void dispose() {
        if (!handleResponseExecutor.isShutdown()) {
            handleResponseExecutor.shutdown();
        }

        if (!collectExecutor.isShutdown()) {
            collectExecutor.shutdown();
        }

        if (!commandExecutor.isShutdown()) {
            commandExecutor.shutdown();
        }
    }

    /**
     * Method for scheduling battery data collect
     *
     * @param runnable collect task
     */
    public ScheduledFuture scheduleCollect(Runnable runnable) {
        ScheduledFuture<?> scheduledFuture = collectExecutor.scheduleAtFixedRate(runnable, 0L, collectPeriod,
                TimeUnit.MILLISECONDS);
        return scheduledFuture;
    }

    /**
     * Method for scheduling command timeout check
     *
     * @param runnable timeout check task
     */
    public ScheduledFuture scheduleTimeoutCheck(Runnable runnable) {
        ScheduledFuture<?> scheduledFuture = collectExecutor.scheduleAtFixedRate(runnable, 0L, timeoutCheckPeriod,
                TimeUnit.MILLISECONDS);
        return scheduledFuture;
    }

    /**
     * Method for failed port check
     *
     * @param runnable failed port check task
     */
    public ScheduledFuture scheduleFailedPortCheck(Runnable runnable) {
        ScheduledFuture<?> scheduledFuture = failedPortCheckExecutor.scheduleAtFixedRate(runnable, 0L, FAILED_PORT_CHECK_PERIOD,
                TimeUnit.MILLISECONDS);
        return scheduledFuture;
    }

    /**
     * Method for sending command task
     *
     * @param runnable send command task
     */
    public void submitSendCommand(Runnable runnable) {
        commandExecutor.submit(runnable);
    }

    /**
     * Submit handle response task
     *
     * @param runnable
     */
    public void submitHandleResponseTask(Runnable runnable) {
        handleResponseExecutor.submit(runnable);
    }

    /**
     * execute websocket push task
     * @param task
     */
    public void executeWebsocketPushTask(Runnable task) {
        websocketExecutor.execute(task);
    }
}
